//#define BOOST_TEST_DYN_LINK

#include "common.h"

#include <TensorDense.h>

BOOST_AUTO_TEST_SUITE(TensorDense_Tests)

BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Deflt_Constructor, TEST_TYPE, numeric_types)
{
    cmpMat::TensorDense<TEST_TYPE> tensor;
    BOOST_CHECK(tensor.size() == 0);
    BOOST_CHECK_THROW(tensor.size0(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size1(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size2(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size3(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size4(), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.size(0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(2), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(4), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.empty());

    BOOST_CHECK(tensor.Dimensions().size() == 0);

    BOOST_CHECK(tensor.rank() == 0);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Rank1_Constructor, TEST_TYPE, numeric_types)
{
    int size1 = 10;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1);
    BOOST_CHECK(tensor.size() == size1);

    BOOST_CHECK(tensor.size0() == size1);
    BOOST_CHECK_THROW(tensor.size1(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size2(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size3(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size4(), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.size(0) == size1);
    BOOST_CHECK_THROW(tensor.size(1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(2), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(4), cmpMat::exception::Exception);

    BOOST_CHECK(!tensor.empty());

    BOOST_REQUIRE(tensor.Dimensions().size() == 1);
    BOOST_CHECK(tensor.Dimensions()[0] == size1);

    BOOST_CHECK(tensor.rank() == 1);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Rank2_Constructor, TEST_TYPE, numeric_types)
{
    int size1 = 10;
    int size2 = 5;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1, size2);
    BOOST_CHECK(tensor.size() == size1 * size2);

    BOOST_CHECK(tensor.size0() == size1);
    BOOST_CHECK(tensor.size1() == size2);
    BOOST_CHECK_THROW(tensor.size2(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size3(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size4(), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.size(0) == size1);
    BOOST_CHECK(tensor.size(1) == size2);
    BOOST_CHECK_THROW(tensor.size(2), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(4), cmpMat::exception::Exception);

    BOOST_CHECK(!tensor.empty());

    BOOST_REQUIRE(tensor.Dimensions().size() == 2);
    BOOST_CHECK(tensor.Dimensions()[0] == size1);
    BOOST_CHECK(tensor.Dimensions()[1] == size2);

    BOOST_CHECK(tensor.rank() == 2);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Rank3_Constructor, TEST_TYPE, numeric_types)
{
    int size1 = 10;
    int size2 = 5;
    int size3 = 2;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1, size2, size3);
    BOOST_CHECK(tensor.size() == size1 * size2 * size3);

    BOOST_CHECK(tensor.size0() == size1);
    BOOST_CHECK(tensor.size1() == size2);
    BOOST_CHECK(tensor.size2() == size3);
    BOOST_CHECK_THROW(tensor.size3(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size4(), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.size(0) == size1);
    BOOST_CHECK(tensor.size(1) == size2);
    BOOST_CHECK(tensor.size(2) == size3);
    BOOST_CHECK_THROW(tensor.size(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(4), cmpMat::exception::Exception);

    BOOST_CHECK(!tensor.empty());

    BOOST_REQUIRE(tensor.Dimensions().size() == 3);
    BOOST_CHECK(tensor.Dimensions()[0] == size1);
    BOOST_CHECK(tensor.Dimensions()[1] == size2);
    BOOST_CHECK(tensor.Dimensions()[2] == size3);

    BOOST_CHECK(tensor.rank() == 3);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Rank4_Constructor, TEST_TYPE, numeric_types)
{
    int size1 = 10;
    int size2 = 5;
    int size3 = 2;
    int size4 = 3;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1, size2, size3, size4);
    BOOST_CHECK(tensor.size() == size1 * size2 * size3 * size4);

    BOOST_CHECK(tensor.size0() == size1);
    BOOST_CHECK(tensor.size1() == size2);
    BOOST_CHECK(tensor.size2() == size3);
    BOOST_CHECK(tensor.size3() == size4);
    BOOST_CHECK_THROW(tensor.size4(), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.size(0) == size1);
    BOOST_CHECK(tensor.size(1) == size2);
    BOOST_CHECK(tensor.size(2) == size3);
    BOOST_CHECK(tensor.size(3) == size4);

    BOOST_CHECK(!tensor.empty());

    BOOST_REQUIRE(tensor.Dimensions().size() == 4);
    BOOST_CHECK(tensor.Dimensions()[0] == size1);
    BOOST_CHECK(tensor.Dimensions()[1] == size2);
    BOOST_CHECK(tensor.Dimensions()[2] == size3);
    BOOST_CHECK(tensor.Dimensions()[3] == size4);
    BOOST_CHECK(tensor.rank() == 4);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Arbt0_Constructor, TEST_TYPE, numeric_types)
{
    std::vector<cmpMat::CmpMatPtr> size(0,0);
    cmpMat::TensorDense<TEST_TYPE> tensor(size);
    BOOST_CHECK(tensor.size() == 0);
    BOOST_CHECK_THROW(tensor.size0(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size1(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size2(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size3(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size4(), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.size(0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(2), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(4), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.empty());

    BOOST_CHECK(tensor.Dimensions().size() == 0);

    BOOST_CHECK(tensor.rank() == 0);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Arbt1_Constructor, TEST_TYPE, numeric_types)
{
    int size1 = 7;
    cmpMat::CmpMatPtr size[] = {size1};
    std::vector<cmpMat::CmpMatPtr> sizeVec(size, size+1);
    cmpMat::TensorDense<TEST_TYPE> tensor(sizeVec);
    BOOST_CHECK(tensor.size() == size1);

    BOOST_CHECK(tensor.size0() == size1);
    BOOST_CHECK_THROW(tensor.size1(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size2(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size3(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size4(), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.size(0) == size1);
    BOOST_CHECK_THROW(tensor.size(1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(2), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(4), cmpMat::exception::Exception);

    BOOST_CHECK(!tensor.empty());

    BOOST_REQUIRE(tensor.Dimensions().size() == 1);
    BOOST_CHECK(tensor.Dimensions()[0] == size1);

    BOOST_CHECK(tensor.rank() == 1);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Arbt2_Constructor, TEST_TYPE, numeric_types)
{
    int size1 = 10;
    int size2 = 5;
    cmpMat::CmpMatPtr size[] = {size1, size2};
    std::vector<cmpMat::CmpMatPtr> sizeVec(size, size+2);
    cmpMat::TensorDense<TEST_TYPE> tensor(sizeVec);
    BOOST_CHECK(tensor.size() == size1 * size2);

    BOOST_CHECK(tensor.size0() == size1);
    BOOST_CHECK(tensor.size1() == size2);
    BOOST_CHECK_THROW(tensor.size2(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size3(), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size4(), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.size(0) == size1);
    BOOST_CHECK(tensor.size(1) == size2);
    BOOST_CHECK_THROW(tensor.size(2), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.size(4), cmpMat::exception::Exception);

    BOOST_CHECK(!tensor.empty());

    BOOST_REQUIRE(tensor.Dimensions().size() == 2);
    BOOST_CHECK(tensor.Dimensions()[0] == size1);
    BOOST_CHECK(tensor.Dimensions()[1] == size2);

    BOOST_CHECK(tensor.rank() == 2);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Arbt5_Constructor, TEST_TYPE, numeric_types)
{
    int size1 = 7;
    int size2 = 5;
    int size3 = 2;
    int size4 = 3;
    int size5 = 2;
    cmpMat::CmpMatPtr size[] = {size1, size2, size3, size4, size5};
    std::vector<cmpMat::CmpMatPtr> sizeVec(size, size+5);
    cmpMat::TensorDense<TEST_TYPE> tensor(sizeVec);
    BOOST_CHECK(tensor.size() == size1 * size2 * size3 * size4 * size5);

    BOOST_CHECK(tensor.size0() == size1);
    BOOST_CHECK(tensor.size1() == size2);
    BOOST_CHECK(tensor.size2() == size3);
    BOOST_CHECK(tensor.size3() == size4);
    BOOST_CHECK(tensor.size4() == size5);

    BOOST_CHECK(tensor.size(0) == size1);
    BOOST_CHECK(tensor.size(1) == size2);
    BOOST_CHECK(tensor.size(2) == size3);
    BOOST_CHECK(tensor.size(3) == size4);
    BOOST_CHECK(tensor.size(4) == size5);

    BOOST_CHECK(!tensor.empty());

    BOOST_REQUIRE(tensor.Dimensions().size() == 5);
    BOOST_CHECK(tensor.Dimensions()[0] == size1);
    BOOST_CHECK(tensor.Dimensions()[1] == size2);
    BOOST_CHECK(tensor.Dimensions()[2] == size3);
    BOOST_CHECK(tensor.Dimensions()[3] == size4);
    BOOST_CHECK(tensor.Dimensions()[4] == size5);
    BOOST_CHECK(tensor.rank() == 5);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_At_R1, TEST_TYPE, numeric_types)
{
    int size1 = 3;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1);
    tensor.setElems([](cmpMat::CmpMatPtr num, TEST_TYPE val){return static_cast<TEST_TYPE>(num);});

    BOOST_CHECK(tensor.at(0) == 0);
    BOOST_CHECK(tensor.at(1) == 1);
    BOOST_CHECK(tensor.at(2) == 2);

    BOOST_CHECK_THROW(tensor.at(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(0,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(1,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(2,0), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(3,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4,0), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(0,0,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(1,0,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(2,0,0), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(3,0,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4,0,0), cmpMat::exception::Exception);

    BOOST_CHECK(tensor(0) == 0);
    BOOST_CHECK(tensor(1) == 1);
    BOOST_CHECK(tensor(2) == 2);

    BOOST_CHECK(tensor[0] == 0);
    BOOST_CHECK(tensor[1] == 1);
    BOOST_CHECK(tensor[2] == 2);
#ifndef _DEBUG
    BOOST_CHECK_NO_THROW(tensor(3));
    BOOST_CHECK_NO_THROW(tensor(4));

    BOOST_CHECK_NO_THROW(tensor(0,0));
    BOOST_CHECK_NO_THROW(tensor(1,0));
    BOOST_CHECK_NO_THROW(tensor(2,0));

    BOOST_CHECK_NO_THROW(tensor(3,0));
    BOOST_CHECK_NO_THROW(tensor(4,0));

    BOOST_CHECK_NO_THROW(tensor(0,0,0));
    BOOST_CHECK_NO_THROW(tensor(1,0,0));
    BOOST_CHECK_NO_THROW(tensor(2,0,0));

    BOOST_CHECK_NO_THROW(tensor(3,0,0));
    BOOST_CHECK_NO_THROW(tensor(4,0,0));
#endif
    // vector accessor
    typedef typename std::vector<cmpMat::CmpMatPtr> t;
    cmpMat::CmpMatPtr a1[] = {0};
    t a1v(a1, a1+1);
    BOOST_CHECK(tensor.at(a1v) == 0);
    cmpMat::CmpMatPtr a2[] = {1};
    t a2v(a2, a2+1);
    BOOST_CHECK(tensor.at(a2v) == 1);
    cmpMat::CmpMatPtr a3[] = {2};
    t a3v(a3, a3+1);
    BOOST_CHECK(tensor.at(a3v) == 2);

    cmpMat::CmpMatPtr a4[] = {3};
    t a4v(a4, a4+1);
    BOOST_CHECK_THROW(tensor.at(a4v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a5[] = {4};
    t a5v(a5, a5+1);
    BOOST_CHECK_THROW(tensor.at(a5v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a6[] = {0, 0};
    t a6v(a6, a6+2);
    BOOST_CHECK_THROW(tensor.at(a6v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a7[] = {1, 0};
    t a7v(a7, a7+2);
    BOOST_CHECK_THROW(tensor.at(a7v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a8[] = {2, 0};
    t a8v(a8, a8+2);
    BOOST_CHECK_THROW(tensor.at(a8v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a9[] = {3, 0};
    t a9v(a9, a9+2);
    BOOST_CHECK_THROW(tensor.at(a9v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a10[] = {4, 0};
    t a10v(a10, a10+2);
    BOOST_CHECK_THROW(tensor.at(a10v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a11[] = {0, 0, 0};
    t a11v(a11, a11+3);
    BOOST_CHECK_THROW(tensor.at(a11v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a12[] = {1, 0, 0};
    t a12v(a12, a12+3);
    BOOST_CHECK_THROW(tensor.at(a12v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a13[] = {2, 0, 0};
    t a13v(a13, a13+3);
    BOOST_CHECK_THROW(tensor.at(a13v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a14[] = {3,0,0};
    t a14v(a14, a14+3);
    BOOST_CHECK_THROW(tensor.at(a14v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a15[] = {4,0,0};
    t a15v(a15, a15+3);
    BOOST_CHECK_THROW(tensor.at(a15v), cmpMat::exception::Exception);

    BOOST_CHECK(tensor(a1v) == 0);
    BOOST_CHECK(tensor(a2v) == 1);
    BOOST_CHECK(tensor(a3v) == 2);
#ifndef _DEBUG
    BOOST_CHECK_NO_THROW(tensor(a4v));
    BOOST_CHECK_NO_THROW(tensor(a5v));

    BOOST_CHECK_NO_THROW(tensor(a6v));
    BOOST_CHECK_NO_THROW(tensor(a7v));
    BOOST_CHECK_NO_THROW(tensor(a8v));

    BOOST_CHECK_NO_THROW(tensor(a9v));
    BOOST_CHECK_NO_THROW(tensor(a10v));

    BOOST_CHECK_NO_THROW(tensor(a11v));
    BOOST_CHECK_NO_THROW(tensor(a12v));
    BOOST_CHECK_NO_THROW(tensor(a13v));

    BOOST_CHECK_NO_THROW(tensor(a14v));
    BOOST_CHECK_NO_THROW(tensor(a15v));
#endif
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_At_R2, TEST_TYPE, numeric_types)
{
    int size1 = 3;
    int size2 = 2;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1, size2);
    tensor.setElems([](cmpMat::CmpMatPtr num, TEST_TYPE val){return static_cast<TEST_TYPE>(num);});

    BOOST_CHECK_THROW(tensor.at(0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(2), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.at(0, 0) == 0);
    BOOST_CHECK(tensor.at(1, 0) == 1);
    BOOST_CHECK(tensor.at(2, 0) == 2);

    BOOST_CHECK(tensor.at(0, 1) == 3);
    BOOST_CHECK(tensor.at(1, 1) == 4);
    BOOST_CHECK(tensor.at(2, 1) == 5);

    BOOST_CHECK(tensor[0] == 0);
    BOOST_CHECK(tensor[1] == 1);
    BOOST_CHECK(tensor[2] == 2);
    BOOST_CHECK(tensor[3] == 3);
    BOOST_CHECK(tensor[4] == 4);
    BOOST_CHECK(tensor[5] == 5);

    BOOST_CHECK_THROW(tensor.at(3, 0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4, 0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(0, 2), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(1, 3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(3, 1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4, 1), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(0,0,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(1,0,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(2,0,0), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(3,0,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4,0,0), cmpMat::exception::Exception);

    BOOST_CHECK(tensor(0) == 0);
    BOOST_CHECK(tensor(1) == 1);
    BOOST_CHECK(tensor(2) == 2);

    BOOST_CHECK(tensor(0, 0) == 0);
    BOOST_CHECK(tensor(1, 0) == 1);
    BOOST_CHECK(tensor(2, 0) == 2);

    BOOST_CHECK(tensor(0, 1) == 3);
    BOOST_CHECK(tensor(1, 1) == 4);
    BOOST_CHECK(tensor(2, 1) == 5);
#ifndef _DEBUG
    BOOST_CHECK_NO_THROW(tensor(3));
    BOOST_CHECK_NO_THROW(tensor(4));

    BOOST_CHECK_NO_THROW(tensor(3,0));
    BOOST_CHECK_NO_THROW(tensor(4,0));

    BOOST_CHECK_NO_THROW(tensor(0,0,0));
    BOOST_CHECK_NO_THROW(tensor(1,0,0));
    BOOST_CHECK_NO_THROW(tensor(2,0,0));

    BOOST_CHECK_NO_THROW(tensor(3,0,0));
    BOOST_CHECK_NO_THROW(tensor(4,0,0));
#endif
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_At_R3, TEST_TYPE, numeric_types)
{
    int size1 = 3;
    int size2 = 2;
    int size3 = 2;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1, size2, size3);
    tensor.setElems([](cmpMat::CmpMatPtr num, TEST_TYPE val){return static_cast<TEST_TYPE>(num);});

    BOOST_CHECK_THROW(tensor.at(0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(2), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(0, 0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(1, 0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(2, 0), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(0, 1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(1, 1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(2, 1), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(3, 0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4, 0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(0, 2), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(1, 3), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(3, 1), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4, 1), cmpMat::exception::Exception);

    BOOST_CHECK_THROW(tensor.at(3,0,0), cmpMat::exception::Exception);
    BOOST_CHECK_THROW(tensor.at(4,0,0), cmpMat::exception::Exception);

    BOOST_CHECK(tensor.at(0, 0, 0) == 0);
    BOOST_CHECK(tensor.at(1, 0, 0) == 1);
    BOOST_CHECK(tensor.at(2, 0, 0) == 2);

    BOOST_CHECK(tensor.at(0, 1, 0) == 3);
    BOOST_CHECK(tensor.at(1, 1, 0) == 4);
    BOOST_CHECK(tensor.at(2, 1, 0) == 5);

    BOOST_CHECK(tensor.at(0, 0, 1) == 6);
    BOOST_CHECK(tensor.at(1, 0, 1) == 7);
    BOOST_CHECK(tensor.at(2, 0, 1) == 8);

    BOOST_CHECK(tensor.at(0, 1, 1) == 9);
    BOOST_CHECK(tensor.at(1, 1, 1) == 10);
    BOOST_CHECK(tensor.at(2, 1, 1) == 11);

    BOOST_CHECK(tensor(0) == 0);
    BOOST_CHECK(tensor(1) == 1);
    BOOST_CHECK(tensor(2) == 2);

    BOOST_CHECK(tensor[0] == 0);
    BOOST_CHECK(tensor[1] == 1);
    BOOST_CHECK(tensor[2] == 2);
    BOOST_CHECK(tensor[3] == 3);
    BOOST_CHECK(tensor[4] == 4);
    BOOST_CHECK(tensor[5] == 5);

    BOOST_CHECK(tensor(0, 0) == 0);
    BOOST_CHECK(tensor(1, 0) == 1);
    BOOST_CHECK(tensor(2, 0) == 2);

    BOOST_CHECK(tensor(0, 1) == 3);
    BOOST_CHECK(tensor(1, 1) == 4);
    BOOST_CHECK(tensor(2, 1) == 5);

    BOOST_CHECK(tensor(0, 0, 0) == 0);
    BOOST_CHECK(tensor(1, 0, 0) == 1);
    BOOST_CHECK(tensor(2, 0, 0) == 2);

    BOOST_CHECK(tensor(0, 1, 0) == 3);
    BOOST_CHECK(tensor(1, 1, 0) == 4);
    BOOST_CHECK(tensor(2, 1, 0) == 5);

    BOOST_CHECK(tensor(0, 0, 1) == 6);
    BOOST_CHECK(tensor(1, 0, 1) == 7);
    BOOST_CHECK(tensor(2, 0, 1) == 8);

    BOOST_CHECK(tensor(0, 1, 1) == 9);
    BOOST_CHECK(tensor(1, 1, 1) == 10);
    BOOST_CHECK(tensor(2, 1, 1) == 11);

    BOOST_CHECK(tensor(3) == 3);
    BOOST_CHECK(tensor(4) == 4);

    BOOST_CHECK(tensor(3,0) == 3);
    BOOST_CHECK(tensor(4,0) == 4);

    BOOST_CHECK(tensor(0,0,0) == 0);
    BOOST_CHECK(tensor(1,0,0) == 1);
    BOOST_CHECK(tensor(2,0,0) == 2);

    BOOST_CHECK(tensor(3,0,0) == 3);
    BOOST_CHECK(tensor(4,0,0) == 4);
#ifndef _DEBUG
    BOOST_CHECK(tensor(3,0,0,0) == 3);
    BOOST_CHECK(tensor(4,0,0,0) == 4);
#endif
    // vector accessors
    typedef typename std::vector<cmpMat::CmpMatPtr> t;
    cmpMat::CmpMatPtr a1[] = {0};
    t a1v(a1, a1+1);
    BOOST_CHECK_THROW(tensor.at(a1v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a2[] = {1};
    t a2v(a2, a2+1);
    BOOST_CHECK_THROW(tensor.at(a2v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a3[] = {2};
    t a3v(a3, a3+1);
    BOOST_CHECK_THROW(tensor.at(a3v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a4[] = {3};
    t a4v(a4, a4+1);
    BOOST_CHECK_THROW(tensor.at(a4v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a5[] = {4};
    t a5v(a5, a5+1);
    BOOST_CHECK_THROW(tensor.at(a5v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a6[] = {0, 0};
    t a6v(a6, a6+2);
    BOOST_CHECK_THROW(tensor.at(a6v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a7[] = {1, 0};
    t a7v(a7, a7+2);
    BOOST_CHECK_THROW(tensor.at(a7v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a8[] = {2, 0};
    t a8v(a8, a8+2);
    BOOST_CHECK_THROW(tensor.at(a8v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a9[] = {0, 1};
    t a9v(a9, a9+2);
    BOOST_CHECK_THROW(tensor.at(a9v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a10[] = {1, 1};
    t a10v(a10, a10+2);
    BOOST_CHECK_THROW(tensor.at(a10v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a11[] = {2, 1};
    t a11v(a11, a11+2);
    BOOST_CHECK_THROW(tensor.at(a11v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a12[] = {3, 0};
    t a12v(a12, a12+2);
    BOOST_CHECK_THROW(tensor.at(a12v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a13[] = {4, 0};
    t a13v(a13, a13+2);
    BOOST_CHECK_THROW(tensor.at(a13v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a14[] = {0, 2};
    t a14v(a14, a14+2);
    BOOST_CHECK_THROW(tensor.at(a14v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a15[] = {1, 3};
    t a15v(a15, a15+2);
    BOOST_CHECK_THROW(tensor.at(a15v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a16[] = {3, 1};
    t a16v(a16, a16+2);
    BOOST_CHECK_THROW(tensor.at(a16v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a17[] = {4, 1};
    t a17v(a17, a17+2);
    BOOST_CHECK_THROW(tensor.at(a17v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a21[] = {3,0,0};
    t a21v(a21, a21+3);
    BOOST_CHECK_THROW(tensor.at(a21v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a22[] = {4,0,0};
    t a22v(a22, a22+3);
    BOOST_CHECK_THROW(tensor.at(a22v), cmpMat::exception::Exception);

    cmpMat::CmpMatPtr a23[] = {0,0,0};
    t a23v(a23, a23+3);
    BOOST_CHECK(tensor.at(a23v) == 0);
    cmpMat::CmpMatPtr a24[] = {1,0,0};
    t a24v(a24, a24+3);
    BOOST_CHECK(tensor.at(a24v) == 1);
    cmpMat::CmpMatPtr a25[] = {2,0,0};
    t a25v(a25, a25+3);
    BOOST_CHECK(tensor.at(a25v) == 2);

    cmpMat::CmpMatPtr a26[] = {0,1,0};
    t a26v(a26, a26+3);
    BOOST_CHECK(tensor.at(a26v) == 3);
    cmpMat::CmpMatPtr a27[] = {1,1,0};
    t a27v(a27, a27+3);
    BOOST_CHECK(tensor.at(a27v) == 4);
    cmpMat::CmpMatPtr a28[] = {2,1,0};
    t a28v(a28, a28+3);
    BOOST_CHECK(tensor.at(a28v) == 5);

    cmpMat::CmpMatPtr a29[] = {0,0,1};
    t a29v(a29, a29+3);
    BOOST_CHECK(tensor.at(a29v) == 6);
    cmpMat::CmpMatPtr a30[] = {1,0,1};
    t a30v(a30, a30+3);
    BOOST_CHECK(tensor.at(a30v) == 7);
    cmpMat::CmpMatPtr a31[] = {2,0,1};
    t a31v(a31, a31+3);
    BOOST_CHECK(tensor.at(a31v) == 8);

    cmpMat::CmpMatPtr a32[] = {0,1,1};
    t a32v(a32, a32+3);
    BOOST_CHECK(tensor.at(a32v) == 9);
    cmpMat::CmpMatPtr a33[] = {1,1,1};
    t a33v(a33, a33+3);
    BOOST_CHECK(tensor.at(a33v) == 10);
    cmpMat::CmpMatPtr a34[] = {2,1,1};
    t a34v(a34, a34+3);
    BOOST_CHECK(tensor.at(a34v) == 11);

    cmpMat::CmpMatPtr a35[] = {3,0,0,0};
    t a35v(a35, a35+4);
    BOOST_CHECK_THROW(tensor.at(a35v), cmpMat::exception::Exception);
    cmpMat::CmpMatPtr a36[] = {4,0,0,0};
    t a36v(a36, a36+4);
    BOOST_CHECK_THROW(tensor.at(a36v), cmpMat::exception::Exception);

    BOOST_CHECK(tensor(a1v) == 0);
    BOOST_CHECK(tensor(a2v) == 1);
    BOOST_CHECK(tensor(a3v) == 2);

    BOOST_CHECK(tensor(a4v) == 3);
    BOOST_CHECK(tensor(a5v) == 4);

    BOOST_CHECK(tensor(a6v) == 0);
    BOOST_CHECK(tensor(a7v) == 1);
    BOOST_CHECK(tensor(a8v) == 2);

    BOOST_CHECK(tensor(a9v) == 3);

    BOOST_CHECK(tensor(a10v) == 4);
    BOOST_CHECK(tensor(a11v) == 5);

    BOOST_CHECK(tensor(a12v) == 3);
    BOOST_CHECK(tensor(a13v) == 4);

    BOOST_CHECK(tensor(a14v) == 6);

    BOOST_CHECK(tensor(a15v) == 10);
    BOOST_CHECK(tensor(a16v) == 6);
    BOOST_CHECK(tensor(a17v) == 7);

    BOOST_CHECK(tensor(a21v) == 3);
    BOOST_CHECK(tensor(a22v) == 4);

    BOOST_CHECK(tensor(a23v) == 0);
    BOOST_CHECK(tensor(a24v) == 1);
    BOOST_CHECK(tensor(a25v) == 2);

    BOOST_CHECK(tensor(a26v) == 3);
    BOOST_CHECK(tensor(a27v) == 4);
    BOOST_CHECK(tensor(a28v) == 5);

    BOOST_CHECK(tensor(a29v) == 6);
    BOOST_CHECK(tensor(a30v) == 7);
    BOOST_CHECK(tensor(a31v) == 8);

    BOOST_CHECK(tensor(a32v) == 9);
    BOOST_CHECK(tensor(a33v) == 10);
    BOOST_CHECK(tensor(a34v) == 11);
#ifndef _DEBUG
    BOOST_CHECK(tensor(a35v) == 3);
    BOOST_CHECK(tensor(a36v) == 4);
#endif
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_FrontBack_R1, TEST_TYPE, numeric_types)
{
    int size1 = 3;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1);
    tensor.setElems([](cmpMat::CmpMatPtr num, TEST_TYPE val){return static_cast<TEST_TYPE>(num);});

    BOOST_CHECK(tensor.front() == 0);
    BOOST_CHECK(tensor.back() == 2);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_FrontBack_R3, TEST_TYPE, numeric_types)
{
    int size1 = 3;
    int size2 = 2;
    int size3 = 2;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1, size2, size3);
    tensor.setElems([](cmpMat::CmpMatPtr num, TEST_TYPE val){return static_cast<TEST_TYPE>(num);});

    BOOST_CHECK(tensor.front() == 0);
    tensor.front() = 42;
    BOOST_CHECK(tensor.front() == 42);
    BOOST_CHECK(tensor.back() == 11);
    tensor.back() = 42;
    BOOST_CHECK(tensor.back() == 42);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Fill, TEST_TYPE, numeric_types)
{
    int size1 = 3;
    int size2 = 2;
    int size3 = 2;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1, size2, size3);
    tensor.fill(42);

    for (cmpMat::CmpMatPtr i = 0; i < tensor.size(); i++)
    {
        BOOST_CHECK(tensor[i] == 42);
    }
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Parse_MAT_float, TEST_TYPE, float_types)
{
    BOOST_CHECK_THROW(
        cmpMat::TensorDense<TEST_TYPE>::loadFromMAT("[ 11.3z, 2 ,3 ,4 ;1 ,2 , 33 , 4;1 ,2 , 3, 44]"), cmpMat::exception::Exception);

    cmpMat::TensorDense<TEST_TYPE> tensor = 
        cmpMat::TensorDense<TEST_TYPE>::loadFromMAT("[ 11.3, 2 ,3 ,4 ;1 ,2 , 33 , 4;1 ,2 , 3, 44]");
    
    std::istringstream sstr("[ 11.3, 2 ,3 ,4 ;1 ,2 , 33 , 4;1 ,2 , 3, 44]");
    cmpMat::TensorDense<TEST_TYPE> tensor2 = 
        cmpMat::TensorDense<TEST_TYPE>::loadFromMAT(sstr);
    BOOST_CHECK(tensor == tensor2);

    BOOST_CHECK(tensor.size() == 12);
    BOOST_CHECK(tensor.size0() == 3);
    BOOST_CHECK(tensor.size1() == 4);

    BOOST_CHECK(std::abs(tensor(0,0) - 11.3) < 1e-5);
    BOOST_CHECK(tensor(0,1) == 2);
    BOOST_CHECK(tensor(0,2) == 3);
    BOOST_CHECK(tensor(0,3) == 4);

    BOOST_CHECK(tensor(1,0) == 1);
    BOOST_CHECK(tensor(1,1) == 2);
    BOOST_CHECK(tensor(1,2) == 33);
    BOOST_CHECK(tensor(1,3) == 4);

    BOOST_CHECK(tensor(2,0) == 1);
    BOOST_CHECK(tensor(2,1) == 2);
    BOOST_CHECK(tensor(2,2) == 3);
    BOOST_CHECK(tensor(2,3) == 44);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Parse_MAT_int, TEST_TYPE, int_types)
{
    BOOST_CHECK_THROW(
        cmpMat::TensorDense<TEST_TYPE>::loadFromMAT("[ 11.3, 2 ,3 ,4 ;1 ,2 , 33 , 4;1 ,2 , 3, 44]"), cmpMat::exception::Exception);

    cmpMat::TensorDense<TEST_TYPE> tensor = 
        cmpMat::TensorDense<TEST_TYPE>::loadFromMAT("[ 11, 2 ,3 ,4 ;1 ,2 , 33 , 4;1 ,2 , 3, 44]");
    
    std::istringstream sstr("[ 11, 2 ,3 ,4 ;1 ,2 , 33 , 4;1 ,2 , 3, 44]");
    cmpMat::TensorDense<TEST_TYPE> tensor2 = 
        cmpMat::TensorDense<TEST_TYPE>::loadFromMAT(sstr);
    BOOST_CHECK(tensor == tensor2);

    BOOST_CHECK(tensor.size() == 12);
    BOOST_CHECK(tensor.size0() == 3);
    BOOST_CHECK(tensor.size1() == 4);

    BOOST_CHECK(tensor(0,0) == 11);
    BOOST_CHECK(tensor(0,1) == 2);
    BOOST_CHECK(tensor(0,2) == 3);
    BOOST_CHECK(tensor(0,3) == 4);

    BOOST_CHECK(tensor(1,0) == 1);
    BOOST_CHECK(tensor(1,1) == 2);
    BOOST_CHECK(tensor(1,2) == 33);
    BOOST_CHECK(tensor(1,3) == 4);

    BOOST_CHECK(tensor(2,0) == 1);
    BOOST_CHECK(tensor(2,1) == 2);
    BOOST_CHECK(tensor(2,2) == 3);
    BOOST_CHECK(tensor(2,3) == 44);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_AssignmentCopy, TEST_TYPE, numeric_types)
{
    int size1 = 3;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1);
    tensor.setElems([](cmpMat::CmpMatPtr num, TEST_TYPE val){return static_cast<TEST_TYPE>(num);});

    cmpMat::TensorDense<TEST_TYPE> tensor2(tensor);
    cmpMat::TensorDense<TEST_TYPE> tensor3;
    tensor3 = tensor;

    BOOST_CHECK(tensor2.at(0) == 0);
    BOOST_CHECK(tensor2.at(1) == 1);
    BOOST_CHECK(tensor2.at(2) == 2);

    BOOST_CHECK(tensor3.at(0) == 0);
    BOOST_CHECK(tensor3.at(1) == 1);
    BOOST_CHECK(tensor3.at(2) == 2);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Move, TEST_TYPE, numeric_types)
{
    int size1 = 3;
    cmpMat::TensorDense<TEST_TYPE> tensor3;
    tensor3 = cmpMat::TensorDense<TEST_TYPE>(size1);
    BOOST_CHECK(tensor3.size() == size1);

    auto func = [](int size) -> cmpMat::TensorDense<TEST_TYPE>
    {
        cmpMat::TensorDense<TEST_TYPE> var(size);
        var.setElems([](int i, TEST_TYPE t)->TEST_TYPE{ return i;});
        return var;
    };
    cmpMat::TensorDense<TEST_TYPE> tensor2(func(size1));
    BOOST_CHECK(tensor2.size() == size1);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(TensorDense_Equal, TEST_TYPE, numeric_types)
{
    int size1 = 10;
    cmpMat::TensorDense<TEST_TYPE> tensor(size1);
    tensor.setElems([](int i, TEST_TYPE t){return static_cast<TEST_TYPE>(i);});
    cmpMat::TensorDense<TEST_TYPE> tensor2(size1);
    tensor2.setElems([](int i, TEST_TYPE t){return static_cast<TEST_TYPE>(i);});

    BOOST_CHECK(tensor == tensor2);
    tensor2(3) = 5;
    BOOST_CHECK(tensor != tensor2);

    int size2 = 2;
    cmpMat::TensorDense<TEST_TYPE> tensor3(size1, size2);
    tensor3.setElems([](int i, TEST_TYPE t){return static_cast<TEST_TYPE>(i);});
    cmpMat::TensorDense<TEST_TYPE> tensor4(size1, size2);
    tensor4.setElems([](int i, TEST_TYPE t){return static_cast<TEST_TYPE>(i);});

    BOOST_CHECK(tensor3 == tensor4);
    tensor4(0, 1) = 5;
    BOOST_CHECK(tensor3 != tensor4);

    int size3 = 2;
    cmpMat::TensorDense<TEST_TYPE> tensor5(size1, size2, size3);
    tensor5.setElems([](int i, TEST_TYPE t){return static_cast<TEST_TYPE>(i);});
    cmpMat::TensorDense<TEST_TYPE> tensor6(size1, size2, size3);
    tensor6.setElems([](int i, TEST_TYPE t){return static_cast<TEST_TYPE>(i);});

    BOOST_CHECK(tensor5 == tensor6);
    tensor6(1, 0, 0) = 5;
    BOOST_CHECK(tensor5 != tensor6);
}

BOOST_AUTO_TEST_SUITE_END()
